import pandas as pd
from pandas import DataFrame
import DataMiningtools as DMTS

class KNearestNeighbor (): #封装了KNN算法的实现
    DMT = DMTS.DataMiningTools()

    def __init__(self,train,k,categorization) -> None:
        self.train = train
        self.k = k
        self.categorization = categorization
        self.a_num_arr = self.DMT.AttributeMax(self.train)
        self.c_num = self.a_num_arr[self.categorization]


    def Distence (self,sample,data): #标称型变量距离
        F = 0 #记录不匹配数
        c_name = self.train.iloc[:,self.categorization].name

        data = data.drop(axis=1,columns=[c_name])
        sample = sample.drop(axis=1,columns=[c_name])

        col = data.shape[1]
        for i in range (0,col):
            if sample.iloc[0,i] != data.iloc[0,i]:
                F += 1
        
        distence = F/col #计算标称型变量距离
        return distence


    def KNN_Model(self,sample): #KNN算法模型
        train_size = self.train.shape[0]
        col = self.train.shape[1]
        train_data = self.train.iloc[0:train_size,0:col]
        train_data['distence'] = 0 #新增distence列,索引为col
        c_count = []

        for i in range (0,self.c_num): #初始化列表c_count，用来存放每类的个数
            c_count.append(0)

        for i in range (0,train_size): 
            data = train_data.iloc[i:i+1,0:col]
            sample_value = sample.iloc[0:1,0:col]
            d = self.Distence(sample_value,data)
            train_data.iloc[i,col] = d
        
        train_data = train_data.sort_values(by='distence')
        train_data = train_data.reset_index()
        train_data = train_data.iloc[:,1:col+2] #按距离排序

        if self.k > train_size:
            self.k = train_size

        for i in range (0,self.k): #统计每个类别出现次数
            t = train_data.iloc[i,self.categorization]
            c_count[t] += 1

        max_c = 0 #记录出现次数最多的类别

        for i in range (0,self.c_num):
            if c_count[i] > c_count[max_c]:
                max_c = i
        return max_c


    def K_Cross(self): #K折交叉验证
        train_size = self.train.shape[0]
        Vdata = pd.DataFrame() #用来存放结果
        count = 0 
        acc_sum = 0 #count和acc_sum用来计算平均正确率

        for start in range (0,train_size,self.k):
            dataset_arr = self.DMT.Sampling(self.train,start,self.k)
            validation_data = dataset_arr[0]
            train_data = dataset_arr[1]

            accuracy = self.DMT.Accuracy_Test\
            (train_data,validation_data,self.c_num,'KNN',self.categorization,self.k,self.a_num_arr)
            
            dict = {"start":start,"stop":start+self.k,"k":self.k,"accuracy":accuracy}
            new_row = pd.DataFrame([dict])
            Vdata = pd.concat([Vdata,new_row])
            acc_sum += accuracy
            count += 1

        Vdata.to_excel('./KNN_Validation.xlsx',index=False)
        return acc_sum / count #返回平均正确率